package virtualstock.game;

import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import virtualstock.exception.GameNotFoundException;
import virtualstock.exception.InsufficientCreditException;
import virtualstock.exception.InsufficientShareException;
import virtualstock.exception.InvalidParameterException;
import virtualstock.exception.PlayerNotFoundException;
import virtualstock.exception.PortfolioNotFoundException;
import virtualstock.exception.StockNotFoundException;
import virtualstock.request.PortfolioController;
import virtualstock.request.StockController;
import virtualstock.request.TransactionController;
import virtualstock.util.GameDetails;
import virtualstock.util.PlayerDetails;
import virtualstock.util.PortfolioDetails;
import virtualstock.util.RankEntry;
import virtualstock.util.StockDetails;

/**
 * Servlet implementation class TransactionServlet
 */
public class TransactionServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	@EJB
	private StockController stockController;
	@EJB
	private TransactionController transactionController;
	@EJB
	private PortfolioController portfolioController;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public TransactionServlet() {
        super();
        // TODO Auto-generated constructor stub
    }
    
    private List<RankEntry> getRanks(List<PlayerDetails> playerList, Long gameID) throws InvalidParameterException, GameNotFoundException, PlayerNotFoundException, PortfolioNotFoundException, StockNotFoundException{
    	List<RankEntry> ranks = new LinkedList<RankEntry>();
    	
    	Long playerID = null;
    	String firstName = null;
    	String lastName = null;
    	BigDecimal portfolioValue = null;
    	for(PlayerDetails player : playerList){
    		playerID = player.getPlayerID();
    		firstName = player.getFirstName();
    		lastName = player.getLastName();
    		//calculate portfolio value
    		PortfolioDetails portfolio = portfolioController.getPortfolioDetailsByGameAndPlayer(gameID, playerID);
    		BigDecimal sumValue = new BigDecimal(0);
    		BigDecimal currentBalance = portfolio.getBalance();
    		//calculate stock value
    		Map<Long, Integer> stockShares = transactionController.getStocksInPortfolio(portfolio.getPortfolioID());
    		for(Map.Entry<Long, Integer> entry : stockShares.entrySet()){
    			Long stockID = entry.getKey();
    			int share = entry.getValue();
    			StockDetails stock = stockController.getStockDetails(stockID);
    			BigDecimal value = stock.getCurrentPrice().multiply(new BigDecimal(share));
    			sumValue = sumValue.add(value);
    		}
    		sumValue = sumValue.add(currentBalance);
    		RankEntry rankEntry = new RankEntry(playerID, lastName, firstName, sumValue);
    		ranks.add(rankEntry);
    	}
    	Collections.sort(ranks);
    	return ranks;
    }
    
    private void display(Long portfolioID, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		
		PortfolioDetails portfolioDetails = null;
		HashMap<Long, Integer> stockIDShareMap = null;
		try {
			portfolioDetails = portfolioController.getPortfolioDetails(portfolioID);
			stockIDShareMap = transactionController.getStocksInPortfolio(portfolioDetails.getPortfolioID());
		} catch (InvalidParameterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (PortfolioNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (StockNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		out.println("<br>");
		out.println("Portfolio");
		out.println("<br>");
		out.println("<table id=\"portfolioInformation\" border=\"3\">");
		out.println("<tr >");
		out.println("    <th bgcolor=>Portfolio ID</th>");
		out.println("    <th bgcolor=>Game ID</th>");
		out.println("    <th bgcolor=>Player ID</th>");
		out.println("    <th bgcolor=>Balance</th>");
		out.println("</tr>");
		out.println("<tr>");
		out.println("    <td>" + portfolioDetails.getPortfolioID() + "&nbsp;&nbsp;</td>"); 
		out.println("    <td>" + portfolioDetails.getGameID() + "&nbsp;&nbsp;</td> ");
		out.println("    <td>" + portfolioDetails.getPlayerID() + "&nbsp;&nbsp;</td> ");
		out.println("    <td>" + portfolioDetails.getBalance() + "&nbsp;&nbsp;</td>");
		out.println("</tr> ");
		out.println("</table>");
		out.println("");
		out.println("");
		
		
		out.println("<br><br><br><br>");
		out.println("All stocks in this portfolio");
		out.println("<br><br>");
		out.println("<table id=\"stocksInPortfolioTable\" border=\"3\">");
		out.println("<tr >");
		out.println("    <th bgcolor=>Stock ID</th>");
		out.println("    <th bgcolor=>Stock Name</th>");
		out.println("    <th bgcolor=>Current Price</th>");
		out.println("    <th bgcolor=>Share</th>");
		out.println("</tr>");
		out.println("<c:forEach var=\"stock\" begin=\"0\" items=\"${requestScope.stockShareMap}\">");
		for (Iterator<Long> it = stockIDShareMap.keySet().iterator(); it.hasNext(); ) {
			Long stockID = it.next();
			if (stockIDShareMap.get(stockID) <= 0) {
				continue;
			}
			StockDetails details;
			try {
				details = stockController.getStockDetails(stockID);
				System.out.println("share: " + stockIDShareMap.containsKey(stockID));
				out.println("<tr>");
				out.println("    <td>" + details.getStockID() + "&nbsp;&nbsp;</td>"); 
				out.println("    <td>" + details.getStockName() + "&nbsp;&nbsp;</td>");
				out.println("    <td>" + details.getCurrentPrice() + "&nbsp;&nbsp;</td>");
				out.println("    <td>" + stockIDShareMap.get(stockID) + "&nbsp;&nbsp;</td> ");
				out.println("</tr> ");
			} catch (InvalidParameterException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (StockNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		out.println("</table>");
		
		out.println("<br><br><br><br>");
		out.println("<h>The rank list of this game</h>");
		out.println("<br><br>");
		out.println("<table id=\"rankListTable\" border=\"3\">");
		out.println("<tr>");
		out.println("<th bgcolor=>Player ID</th>");
		out.println("<th bgcolor=>First Name</th>");
		out.println("<th bgcolor=>Last Name</th>");
		out.println("		    <th bgcolor=>Portfolio Value</th>");
		out.println("		</tr>");
		//		<c:forEach var="rank" begin="0" items="${requestScope.rankList}">
		GameDetails currentGame = (GameDetails) request.getSession().getAttribute("currentGame");
		List<PlayerDetails> playerList = (List<PlayerDetails>) request.getSession().getAttribute("playerListInGame");
		List<RankEntry> ranks = null;
		
		try {
			ranks = this.getRanks(playerList, currentGame.getGameID());
		} catch (InvalidParameterException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (GameNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (PlayerNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (PortfolioNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (StockNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if(ranks != null){
			for(RankEntry rank : ranks){
				out.println("<tr>");
				out.println("<td>" + rank.getPlayerID() + "</td>");
				out.println("<td>" + rank.getFirstName() + "</td>");
				out.println("<td>" + rank.getLastName() + "</td>");
				out.println("<td>" + rank.getPortfolioValue() + "</td>");
				out.println("</tr>");
			}
		}
		out.println("</table>"); 	
		
	//			</c:forEach>
						
    }
    
    private void processBuy(Long stockID, int share, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		
		PortfolioDetails portfolioDetails = (PortfolioDetails) request.getSession().getAttribute("portfolio");
		Long portfolioID = null;
    	try {
    		portfolioID = portfolioDetails.getPortfolioID();
    	} catch (Exception e) {
    		out.println("session expired.");
    		return;
    	}
		BigDecimal currentPrice = new BigDecimal(0.00);
		
		try {
			StockDetails stockDetails = stockController.getStockDetails(stockID);
			currentPrice = stockDetails.getCurrentPrice();
			transactionController.buy(portfolioID, stockID, share, currentPrice);
			out.println("buy stock successful <br><br>");
		} catch (InvalidParameterException e) {
			out.print("InvalidParameter <br>");
			e.printStackTrace();
		} catch (StockNotFoundException e) {
			out.print("StockNotFound <br>");
			e.printStackTrace();
		} catch (PortfolioNotFoundException e) {
			out.print("PortfolioNotFound <br>");
			e.printStackTrace();
		} catch (InsufficientCreditException e) {
			out.print("InsufficientCredit <br>");
			e.printStackTrace();
		}
		
		display(portfolioID, request, response);
    }
    
    private void processSell(Long stockID, int share, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	PrintWriter out = response.getWriter();
    	
    	PortfolioDetails portfolioDetails = (PortfolioDetails) request.getSession().getAttribute("portfolio");
    	Long portfolioID = null;
    	try {
    		portfolioID = portfolioDetails.getPortfolioID();
    	} catch (Exception e) {
    		out.println("session expired.");
    		return;
    	}BigDecimal currentPrice = new BigDecimal(0.00);
		
		try {
			StockDetails stockDetails = stockController.getStockDetails(stockID);
			currentPrice = stockDetails.getCurrentPrice();
			transactionController.sell(portfolioID, stockID, share, currentPrice);
			out.println("sell stock successful <br><br>");
		} catch (InvalidParameterException e) {
			out.print("InvalidParameter <br>");
			e.printStackTrace();
		} catch (StockNotFoundException e) {
			out.print("StockNotFound <br>");
			e.printStackTrace();
		} catch (PortfolioNotFoundException e) {
			out.print("PortfolioNotFound <br>");
			e.printStackTrace();
		} catch (InsufficientShareException e) {
			out.print("InsufficientShare <br>");
			e.printStackTrace();
		}
		
		display(portfolioID, request, response);
    }
    
    protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
		PrintWriter out = response.getWriter();

		String type = "";
		Long stockID = null;
		int share = 0;
    	String query = request.getQueryString();
    	//out.print("query: " + query);
		Matcher m = Pattern.compile("type=(\\w+)&stockid=(\\d+)&share=(\\d+)").matcher(query);
		if (m.find()) {
			type = m.group(1);
			stockID = new Long(m.group(2));
			share = Integer.parseInt(m.group(3));
			if (type.equals("buy")) {
				processBuy(stockID, share, request, response);
			} else if (type.equals("sell")) {
				processSell(stockID, share, request, response);
			}
		} else {
			out.print("invalid input");
		}
		response.setHeader("Cache-Control", "no-cache");
    }


	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

}
